%reload_ext autoreload
%autoreload 2
%matplotlib inline
import pandas as pd
import numpy as np
from datetime import datetime
import matplotlib.pyplot as plt
from sklearn import cluster, metrics, decomposition, preprocessing
import plotly.io as pio
pio.renderers.default='notebook'
import plotly.express as px
import plotly.graph_objects as go
# Import des modules contenant les fonctions utilitaires
import src.data_helpers as dth
# Réglage des graphiques
plt.style.use('seaborn-whitegrid')
plt.rc('font', size=12)
plt.rc('axes', titlesize=20)
plt.rc('axes', labelsize=20)
plt.rc('xtick', labelsize=12)
plt.rc('ytick', labelsize=12)
plt.rc('legend', fontsize=12)
dims_fig = (25,20)
orders_merge = pd.read_csv('merge.csv', sep=',')
date_format = '%Y-%m-%d %H:%M:%S'
max_date = datetime.strptime(orders_merge['order_purchase_timestamp'].max(), date_format)
orders_merge['order_purchase_timestamp'] = orders_merge['order_purchase_timestamp'].transform(lambda x: (max_date - datetime.strptime(x, date_format)).days)
On va utiliser un des modèles identifié dans le notebook "model", comme étant le plus performant pour segmenter les clients les plus importants, c'est à dire ceux qui ont passé plus d'une commande ou dépensent de gros montants, ainsi que ceux qui n'ont commandé qu'une seule fois.
Il s'agit du modèle kMeans avec 6 clusters et les features RFM (Récence, Fréquence, Montant cumulé) + une feature qui indique si le pays du client et le même que celui du vendeur.
Cette dernière feature permet de segmenter plus précisément les clients qui n'achètent qu'une seule fois.
def determineStateDifference(row):
return 1 if row['customer_state'] == row['seller_state'] else 0
orders_merge['seller_same_state'] = orders_merge.apply(lambda row: determineStateDifference(row), axis=1)
def getDataForModelisation(data):
return data.groupby('customer_unique_id').agg(
recence = ("order_purchase_timestamp", "min"),
frequence = ("customer_id", "count"),
montant_cumulé = ("payment_value", "sum"),
seller_same_state = ("seller_same_state", "first")
)
On va évaluer la performance de notre modèle au cours du temps sur les données que l'on a en notre possession, en jouant sur la fréquence de mise à jour de ces données.
L'objectif est de trouver à partir de qu'elle fréquence le modèle se dégrade, c'est à dire un seuil à partir duquel les prédictions entre le modèle d'origine et un nouveau modèle entraîné sont trop différentes.
def calculateAriScore(data, time):
result = []
for i in range (500, time, -time):
# t0
f0 = getDataForModelisation(data[data['order_purchase_timestamp'] > i])
s0 = preprocessing.StandardScaler().fit(f0)
f0_std = pd.DataFrame(
s0.transform(f0),
columns=f0.columns
)
m0 = cluster.KMeans(5)
m0.fit(f0_std)
# t1 = t0 + n days
f1 = getDataForModelisation(data[data['order_purchase_timestamp'] > i-time])
s1 = preprocessing.StandardScaler().fit(f1)
f1_std = pd.DataFrame(
s1.transform(f1),
columns=f1.columns
)
m1 = cluster.KMeans(5)
c1 = m1.fit_predict(f1_std)
# Comparaison entre t0 et t1
f1_0_std = pd.DataFrame(
s0.transform(f1),
columns=f1.columns
)
c1_0 = m0.predict(f1_0_std)
score = metrics.adjusted_rand_score(c1_0, c1)
result.append(score)
return result
frequences = [3, 7, 15, 30, 60, 120]
for i in frequences:
result = calculateAriScore(orders_merge, i)
fig = px.line(
result,
height=300,
width=800,
title=f"Score ARI par interval de {i} jours (moyenne = {sum(result)/len(result)})"
)
fig.show()
def calculateAriScoreFromOrigin(data, time):
result = []
# t0
f0 = getDataForModelisation(data[data['order_purchase_timestamp'] > 500])
s0 = preprocessing.StandardScaler().fit(f0)
f0_std = pd.DataFrame(
s0.transform(f0),
columns=f0.columns
)
m0 = cluster.KMeans(5)
m0.fit(f0_std)
for i in range (500, time, -time):
# tn = t0 + n days
f1 = getDataForModelisation(data[data['order_purchase_timestamp'] > i-time])
s1 = preprocessing.StandardScaler().fit(f1)
f1_std = pd.DataFrame(
s1.transform(f1),
columns=f1.columns
)
m1 = cluster.KMeans(5)
c1 = m1.fit_predict(f1_std)
# Comparaison entre t0 et t1
f1_0_std = pd.DataFrame(
s0.transform(f1),
columns=f1.columns
)
c1_0 = m0.predict(f1_0_std)
score = metrics.adjusted_rand_score(c1_0, c1)
result.append(score)
return result
result = calculateAriScoreFromOrigin(orders_merge, 1)
fig = px.line(
result,
title=f"Evolution du score ARI dans le temps",
width=800,
)
fig.show()
Si l'on fixe le seuil du score ARI à 0.8, il faudrait donc réentraîner le modèle tous les 90 jours environ en y ajoutant les nouvelles données.
Si le modèle n'est pas réentraîné dans le temps, le clustering collera de moins en moins aux nouvelles données, et l'on risque de faire de mauvaises prédiction et donc un mauvais ciblage de clientèle.